class LookAheadIterator(object):
    def __init__(self, iterable):
        self.iterable = iter(iterable)
        self.look_ahead = list()
        self.markers = list()
        self.default = None
        self.value = None

    def __iter__(self):
        return self

    def set_default(self, value):
        self.default = value

    def next(self):
        return self.__next__()

    def __next__(self):
        if self.look_ahead:
            self.value = self.look_ahead.pop(0)
        else:
            self.value = next(self.iterable)

        if self.markers:
            self.markers[-1].append(self.value)

        return self.value

    def look(self, i=0):
        """ Look ahead of the iterable by some number of values with advancing
        past them.

        If the requested look ahead is past the end of the iterable then None is
        returned.

        """

        length = len(self.look_ahead)

        if length <= i:
            try:
                self.look_ahead.extend([next(self.iterable)
                    for _ in range(length, i + 1)])
            except StopIteration:
                return self.default

        self.value = self.look_ahead[i]
        return self.value

    def last(self):
        return self.value

    def __enter__(self):
        self.push_marker()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # Reset the iterator if there was an error
        if exc_type or exc_val or exc_tb:
            self.pop_marker(True)
        else:
            self.pop_marker(False)

    def push_marker(self):
        """ Push a marker on to the marker stack """
        self.markers.append(list())

    def pop_marker(self, reset):
        """ Pop a marker off of the marker stack. If reset is True then the
        iterator will be returned to the state it was in before the
        corresponding call to push_marker().

        """

        marker = self.markers.pop()

        if reset:
            # Make the values available to be read again
            marker.extend(self.look_ahead)
            self.look_ahead = marker
        elif self.markers:
            # Otherwise, reassign the values to the top marker
            self.markers[-1].extend(marker)
        else:
            # If there are not more markers in the stack then discard the values
            pass

class LookAheadListIterator(object):
    def __init__(self, iterable):
        self.list = list(iterable)

        self.marker = 0
        self.saved_markers = []

        self.default = None
        self.value = None

    def __iter__(self):
        return self

    def set_default(self, value):
        self.default = value

    def next(self):
        return self.__next__()

    def __next__(self):
        try:
            self.value = self.list[self.marker]
            self.marker += 1
        except IndexError:
            raise StopIteration()

        return self.value

    def look(self, i=0):
        """ Look ahead of the iterable by some number of values with advancing
        past them.

        If the requested look ahead is past the end of the iterable then None is
        returned.

        """

        try:
            self.value = self.list[self.marker + i]
        except IndexError:
            return self.default

        return self.value

    def last(self):
        return self.value

    def __enter__(self):
        self.push_marker()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # Reset the iterator if there was an error
        if exc_type or exc_val or exc_tb:
            self.pop_marker(True)
        else:
            self.pop_marker(False)

    def push_marker(self):
        """ Push a marker on to the marker stack """
        self.saved_markers.append(self.marker)

    def pop_marker(self, reset):
        """ Pop a marker off of the marker stack. If reset is True then the
        iterator will be returned to the state it was in before the
        corresponding call to push_marker().

        """

        saved = self.saved_markers.pop()

        if reset:
            self.marker = saved
        elif self.saved_markers:
            self.saved_markers[-1] = saved


def _col_width(ch: str) -> int:
    """返回字符在“列”上的宽度：空格=1，Tab=8，其余0"""
    if ch == ' ':
        return 1
    if ch == '\t':
        return 8
    return 0


def _leading_white_len(line: str) -> int:
    """计算一行左侧连续空白字符的‘列数’总和"""
    cols = 0
    for ch in line:
        w = _col_width(ch)
        if w == 0:  # 遇到非空白即停止
            break
        cols += w
    return cols


def strip_indent(text: str) -> str:
    """
    等价于 Java Text Block 的“去除公共空白前缀”算法
    返回处理后的字符串，末尾带换行。
    """
    # 1. 按行拆分并统一换行符
    lines = text.replace('\r\n', '\n').replace('\r', '\n').split('\n')

    # 2. 丢弃“仅空白”的首行与末行
    while lines and lines[0].strip() == '':
        lines.pop(0)
    while lines and lines[-1].strip() == '':
        lines.pop()

    if not lines:  # 全是空行
        return ''

    # 3. 计算所有非空行的最小公共空白列数
    non_empty_lens = [_leading_white_len(ln) for ln in lines if ln.strip() != '']
    common_cols = min(non_empty_lens) if non_empty_lens else 0

    # 4. 剃掉公共空白列
    def shave(line: str) -> str:
        shave_cols = 0
        for ch in line:
            if shave_cols >= common_cols:
                break
            w = _col_width(ch)
            if shave_cols + w > common_cols:
                break
            shave_cols += w
        return line[shave_cols:]

    shaved = [shave(ln) for ln in lines]

    # 5. 重新拼成字符串（保留换行）
    return '\n'.join(shaved) + '\n'
